`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2020/10/22 17:58:02
// Design Name: 
// Module Name: alu
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

`define FREE    2'b00
`define GAPE    2'b01
`define DIV_ON  2'b10
`define END     2'b11

module alu(
    input logic                 clk,
    //cp0 relavent  
    input  logic [31: 0]        cp0_reg_data_i,
    input  logic [ 7: 0]        cp0_reg_adr,
    output logic [ 7: 0]        cp0_radr_o,
    output logic                cp0_we_o,
    output logic [31: 0]        cp0_data_o,
    output logic [ 7: 0]        cp0_wadr_o,
    //cp0 relavent end  
    input  logic [ 4: 0]        alu_control,
    input  logic [31: 0]        src_a,src_b, //rs,rt,sa
    input  logic [ 4: 0]        src_c,
    input  logic [31: 0]        hi_i,lo_i,
    input  logic                regwrite_i,
    output logic                regwrite_o,
    output logic [ 1: 0]        hilowrite_e,
    output logic [31: 0]        alu_res,
    input  logic                stall,
    input  logic                mduen,
    output logic                stallreq,
    output logic [63: 0]        hilo_o,
    input  logic [`EXCS_BUS]    excs_i,
    output logic [`EXCS_BUS]    excs_o,
    input  logic                next_is_slot_i,
    output logic                is_slot_o
    );

    logic               ovflow_sum;
    logic               src_a_lt_src_b;
    logic [31: 0]       src_b_mux;
    logic [31: 0]       res_sum;


    //--MDU part-------------------------------------------------------------------
    logic [ 1: 0]       state;
    logic               is_signed;
    logic               res_sign;
    logic [31:0]        opr1;
    logic [31:0]        opr2;
    logic [31:0]        src_a_lock; //锁存除法过程中src_a的值，详见GAPE阶段。
                                    //注意有符号除法余数和被除数符号相同。
    logic               flag; 

    logic               exc_ov;
    //logic               exc_d_ade;

    assign exc_ov = (alu_control == 5'b10111||alu_control == 5'b00110)&&ovflow_sum == 1'b1;

    assign is_slot_o = next_is_slot_i;

    always_comb begin
        excs_o              = excs_i;
        excs_o[`EXC_OV]     = exc_ov;
        //excs_o[`EXC_D_ADE]  = exc_d_ade;
    end

    always_comb begin
        if(~stallreq) begin
            flag        = 1'b0;
        end
        else if(stallreq && flag == 1'b0) begin 
            if(~clk)
                flag    = 1'b1;
            is_signed   = (alu_control == 5'b11111|alu_control == 5'b11101);
            res_sign    = is_signed&(src_a[31]^src_b[31]);
            opr1        = (is_signed&src_a[31])? ~src_a + 1 : src_a;
            opr2        = (is_signed&src_b[31])? ~src_b + 1 : src_b;
            src_a_lock  = src_a;
        end
        else
            flag        = flag;
            is_signed   = is_signed;
            res_sign    = res_sign;
            opr1        = opr1;
            opr2        = opr2;
            src_a_lock  = src_a_lock;
    end

    //--mul part-------------------------------------------------------------------

    // [AB] * [CD] = B*D + (A*C << 32) + ((A*D + C*B) << 16)
    logic [15:0] a; 
    assign a = opr1[31:16];
    logic [15:0] b;
    assign b = opr1[15:0];
    logic [15:0] c; 
    assign c = opr2[31:16];
    logic [15:0] d;
    assign d = opr2[15:0];

    (*use_dsp="yes"*) logic [31:0] ac; 
    assign ac = a*c;
    (*use_dsp="yes"*) logic [31:0] ad; 
    assign ad = a*d;
    (*use_dsp="yes"*) logic [31:0] bc; 
    assign bc = b*c;
    (*use_dsp="yes"*) logic [31:0] bd;
    assign bd = b*d;

    logic [15: 0] tail;
    logic [33: 0] mid;
    logic [15: 0] top;
    logic [ 1: 0] mid_carry;
    assign mid_carry    = mid[33:32];

    assign tail         = bd[15: 0];
    assign mid          = ad + bc + {ac[15: 0], bd[31:16]};
    assign top          = ac[31:16] + mid_carry;

    logic [63:0] res_abs;
    assign res_abs      = {top, mid[31:0], tail};
    logic [63:0] res;
    assign res          = res_sign?(~res_abs+1): res_abs;
    //--mul part end---------------------------------------------------------------

    //--div part ------------------------------------------------------------------
    logic   [32:0]  div_temp;
    logic   [5: 0]  cnt;
    logic   [64:0]  dividend;
    logic   [31:0]  divisor;     
    logic   [5: 0]  bit_dividend;           // dividend empty bits
    logic   [5: 0]  bit_divisor_remain;     // divisor has bits
    logic   [5: 0]  bit_dividend_remain;
    logic   [5: 0]  bit_move;               // caculate begin move bits
    logic   [5: 0]  bit_move_subone;
    logic   [5: 0]  cntplusone;
    logic           div_resultZero;         // opr1 bits < opr2 bits
    logic   [64:0]  tmp_dividend;           // dividend calc before move in

    assign bit_dividend = opr1[31] ?  0 : opr1[30] ? 1  : opr1[29] ? 2  :
                          opr1[28] ?  3 : opr1[27] ? 4  : opr1[26] ? 5  :
                          opr1[25] ?  6 : opr1[24] ? 7  : opr1[23] ? 8  : 
                          opr1[22] ?  9 : opr1[21] ? 10 : opr1[20] ? 11 :
                          opr1[19] ? 12 : opr1[18] ? 13 : opr1[17] ? 14 : 
                          opr1[16] ? 15 : opr1[15] ? 16 : opr1[14] ? 17 : 
                          opr1[13] ? 18 : opr1[12] ? 19 : opr1[11] ? 20 :
                          opr1[10] ? 21 : opr1[ 9] ? 22 : opr1[ 8] ? 23 : 
                          opr1[ 7] ? 24 : opr1[ 6] ? 25 : opr1[ 5] ? 26 : 
                          opr1[ 4] ? 27 : opr1[ 3] ? 28 : opr1[ 2] ? 29 : 
                          opr1[ 1] ? 30 : opr1[ 0] ? 31 : 32 ;

    assign bit_divisor_remain = opr2[31] ? 32 : opr2[30] ? 31 : opr2[29] ? 30 :
                                opr2[28] ? 29 : opr2[27] ? 28 : opr2[26] ? 27 :
                                opr2[25] ? 26 : opr2[24] ? 25 : opr2[23] ? 24 : 
                                opr2[22] ? 23 : opr2[21] ? 22 : opr2[20] ? 21 :
                                opr2[19] ? 20 : opr2[18] ? 19 : opr2[17] ? 18 : 
                                opr2[16] ? 17 : opr2[15] ? 16 : opr2[14] ? 15 : 
                                opr2[13] ? 14 : opr2[12] ? 13 : opr2[11] ? 12 :
                                opr2[10] ? 11 : opr2[ 9] ? 10 : opr2[ 8] ? 9 : 
                                opr2[ 7] ?  8 : opr2[ 6] ?  7 : opr2[ 5] ? 6 : 
                                opr2[ 4] ?  5 : opr2[ 3] ?  4 : opr2[ 2] ? 3 : 
                                opr2[ 1] ?  2 : opr2[ 0] ?  1 : 0 ;

    assign bit_dividend_remain = opr1[31] ? 32 : opr1[30] ? 31 : opr1[29] ? 30 :
                                 opr1[28] ? 29 : opr1[27] ? 28 : opr1[26] ? 27 :
                                 opr1[25] ? 26 : opr1[24] ? 25 : opr1[23] ? 24 : 
                                 opr1[22] ? 23 : opr1[21] ? 22 : opr1[20] ? 21 :
                                 opr1[19] ? 20 : opr1[18] ? 19 : opr1[17] ? 18 : 
                                 opr1[16] ? 17 : opr1[15] ? 16 : opr1[14] ? 15 : 
                                 opr1[13] ? 14 : opr1[12] ? 13 : opr1[11] ? 12 :
                                 opr1[10] ? 11 : opr1[ 9] ? 10 : opr1[ 8] ?  9 : 
                                 opr1[ 7] ?  8 : opr1[ 6] ?  7 : opr1[ 5] ?  6 : 
                                 opr1[ 4] ?  5 : opr1[ 3] ?  4 : opr1[ 2] ?  3 : 
                                 opr1[ 1] ?  2 : opr1[ 0] ?  1 : 0 ;                     

    assign div_temp = {1'b0,dividend[63:32]} - {1'b0,divisor};

    reg    [5: 0]  reg_bit_dividend;
    reg    [5: 0]  reg_bit_divisor_remain;
    reg    [5: 0]  reg_bit_dividend_remain;
        
    // this part is use DivFree
    assign div_resultZero   = reg_bit_divisor_remain > reg_bit_dividend_remain;
    // DivFree part end

    // this part is use DivPrepare
    assign bit_move         = reg_bit_dividend + reg_bit_divisor_remain;
    assign bit_move_subone  = bit_move - 1;
    assign tmp_dividend     = 64'b0 | (opr1 << bit_move);               // may cause high delay
    // DivPrepare part end
    wire                    remainder_sign;
    wire [31:0]             tmp_div_result;
    wire [31:0]             tmp_remainder;

    //assign remainder_sign   = is_signed  & (opr1[31] ^ dividend[64]);
    assign remainder_sign   = is_signed & src_a_lock[31];
    assign tmp_div_result   = (~dividend[31:0] + 1);
    assign tmp_remainder    = (~dividend[64:33] + 1);
    // this part is use DivOn
    assign cntplusone       = cnt + 1;



    //--div part end---------------------------------------------------------------

    always @(posedge clk) begin
        if(!mduen) begin
            state <= `FREE;
        end 
        else begin
            case(state)              
                `FREE:  begin
                    case(alu_control)  //5'b11111 mult  5'b11110 multu 
                        5'b11111, //mult
                        5'b11110: begin //multu
                            state <= `END;
                        end

                        5'b11101, //div
                        5'b11100: begin //divu
                            reg_bit_dividend        <= bit_dividend;
                            reg_bit_divisor_remain  <= bit_divisor_remain;
                            reg_bit_dividend_remain <= bit_dividend_remain;
                            state                   <= `GAPE;
                        end

                    endcase
                end

                `GAPE:  begin
                    case(alu_control)
                        5'b11101,
                        5'b11100: begin
                            if(opr2 == 32'b0) begin
                                dividend <= 65'b0;
                                state <= `END;
                            end
                            else if(div_resultZero) begin
                                state <= `END;
                                dividend <= {src_a_lock, 33'b0};
                            end
                            else begin
                                state <= `DIV_ON;
                                cnt <= bit_move_subone;
                                dividend <= tmp_dividend;
                                divisor <= opr2;
                            end
                        end
                    endcase
                end 

                `DIV_ON: begin
                    if(cnt != 6'b100000) begin
                        if(div_temp[32] == 1'b1) begin
                            dividend <= {dividend[63:0], 1'b0};
                        end
                        else begin
                            dividend <= {div_temp[31:0], dividend[31:0], 1'b1};
                        end
                        cnt <= cntplusone;
                    end 
                    else begin
                        if(res_sign) begin
                            dividend[31:0] <= tmp_div_result;
                        end
                        if(remainder_sign) begin              
                            dividend[64:33] <= tmp_remainder;
                        end
                        state <= `END;
                        cnt <= 6'b0; 
                    end
                end

                `END: begin
                    if(stall == 1'b0) begin
                        state <= `FREE;
                    end
                end

                default: begin
                    state <= `FREE;
                end 
            endcase
        end
    end
    


    //--MDU part end---------------------------------------------------------------

    assign src_b_mux = ((alu_control == 5'b00110) || //sub
                        (alu_control == 5'b10011) || //subu
                        (alu_control == 5'b00111) //slt、slti
                        )?  (~src_b)+1 : src_b;

    assign res_sum = src_a + src_b_mux;

    assign ovflow_sum = ((!src_a[31]&&!src_b_mux[31])&&res_sum[31]) ||
                        ((src_a[31]&&src_b_mux[31])&&!res_sum[31]);

    assign src_a_lt_src_b = (alu_control == 5'b00111)? //slt,成立表示有符号比较
                            ((src_a[31]&&!src_b[31])||
                            (!src_a[31]&&!src_b[31]&&res_sum[31])||
                            (src_a[31]&&src_b[31]&&res_sum[31])) 
                            : (src_a<src_b); //无符号比较直接用比较运算符比较src_a和src_b 
    
//    assign zero = alu_res;

    assign cp0_radr_o = cp0_reg_adr;
    assign cp0_wadr_o = cp0_reg_adr;

    always_comb begin
        cp0_we_o = 1'b0;
        cp0_data_o = 32'b0;
        stallreq = 1'b0;
        hilowrite_e = 2'b00;
        hilo_o = 64'b0;
        alu_res = 32'b0;
        
        if((alu_control == 5'b10111||alu_control == 5'b00110)&&ovflow_sum == 1'b1) //add、sub
            regwrite_o = 1'b0;
        else 
            regwrite_o = regwrite_i;

        if(mduen) begin
            case(alu_control)
                
                5'b11111,
                5'b11110: begin
                    if(state == `END) begin
                        hilowrite_e = 2'b11;
                        hilo_o = res;
                        stallreq = 1'b0;
                    end
                    else begin
                        hilowrite_e = 2'b00;
                        hilo_o = 64'b0;
                        stallreq = 1'b1;
                    end
                end

                5'b11101,
                5'b11100: begin
                    if(state == `END) begin
                        hilowrite_e = 2'b11;
                        hilo_o = {dividend[64:33], dividend[31:0]};
                        stallreq = 1'b0;
                    end
                    else begin
                        hilowrite_e = 2'b00;
                        hilo_o = 64'b0;
                        stallreq = 1'b1;
                    end
                end

            endcase
        end
        else    begin
            case(alu_control)
            //mfc0
            5'b11001:  
                alu_res = cp0_reg_data_i;
            //mtc0
            5'b11010:   begin
                cp0_we_o = 1'b1;
                cp0_data_o = src_b;
            end
            5'b10111:  alu_res = res_sum; //add、addi
            5'b00010:  alu_res = res_sum;//load、store                    
            5'b10010:  alu_res = res_sum;//addu、addiu                   
            5'b00110:  alu_res = res_sum;//sub
            5'b10011:  alu_res = res_sum;//subu
            5'b00111:  alu_res = src_a_lt_src_b;//slt、slti有符号比较
            5'b01100:  alu_res = src_a_lt_src_b;//sltu、sltiu无符号比较
            5'b00000:  alu_res = src_a & src_b;
            5'b00001:  alu_res = src_a | src_b;
            5'b00011:  alu_res = src_a ^ src_b;
            5'b00100:  alu_res = ~(src_a | src_b);
            5'b00101:  alu_res = src_b << src_a[4:0];//sllv
            5'b01101:  alu_res = src_b << src_c[4:0];//sll
            5'b01110:  alu_res = src_b >> src_c[4:0];//srl
            5'b01001:  alu_res = src_b >> src_a[4:0];//srlv
            5'b01111:  alu_res = ({32{src_b[31]}}<<(6'd32 - {1'b0, src_c[4:0]}))
                        | src_b >> src_c[4:0];//sra
            5'b01000:  alu_res = ({32{src_b[31]}}<<(6'd32 - {1'b0, src_a[4:0]}))
                        | src_b >> src_a[4:0];//srav
            5'b01010: begin   //mthi
                        hilo_o = {src_a, 32'b0};
                        hilowrite_e = 2'b01;
                    end
            5'b01011: begin   //mtlo
                        hilo_o = {32'b0, src_a};
                        hilowrite_e = 2'b10;
                    end
            5'b10000: begin  //mfhi
                        alu_res = hi_i;
                        hilowrite_e = 2'b00;
                    end
            5'b10001: begin  //mflo
                        alu_res = lo_i;
                        hilowrite_e = 2'b00;
                    end
            
            endcase
        end
    end

endmodule
